package online.heycm.platform.common.tools.file;

import cn.hutool.core.collection.CollectionUtil;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 压缩工具
 *
 * @author hey
 * @version 1.0
 * @date 2023/12/27 14:45
 */
public class ZipUtil {
    public static final String ZIP_SUFFIX = ".zip";

    private ZipUtil() {
    }

    /**
     * 获取与源文件同目录同名zip文件名
     *
     * @param file 源文件, 如 /home/app.log
     * @return zip文件名, 如 /home/app.log.zip
     */
    public static String getZipSameName(File file) {
        return file.getAbsolutePath() + ZIP_SUFFIX;
    }

    /**
     * 打包文件或目录为zip压缩文件
     *
     * @param file 文件/目录
     * @return zip文件
     */
    public static File fileToZip(File file) {
        File zip = new File(getZipSameName(file));
        try {
            fileToZip(file, zip);
        } catch (Exception e) {
            FileUtil.delete(zip);
            throw new RuntimeException(e);
        }
        return zip;
    }

    /**
     * 压缩文件
     *
     * @param file 源文件/源目录
     * @param zip  压缩文件
     */
    public static void fileToZip(File file, File zip) {
        try (ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(zip.toPath())))) {
            fileToZip(file, zos);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 压缩文件
     *
     * @param file     源文件/源目录
     * @param zos      压缩输出流
     */
    public static void fileToZip(File file, ZipOutputStream zos) {
        try {
            fileToZip(file.getParent(), file, zos);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 压缩文件
     *
     * @param basePath 源文件/源目录基础绝对路径（多层嵌套目录打包时以此路径为基准）, 一般为 file.getParent()
     * @param file     源文件/源目录
     * @param zos      压缩输出流
     * @throws IOException IOException
     */
    private static void fileToZip(String basePath, File file, ZipOutputStream zos) throws IOException {
        if (file.isFile()) {
            String zipEntryName = file.getAbsolutePath().substring(basePath.length() + 1);
            ZipEntry entry = new ZipEntry(zipEntryName);
            zos.putNextEntry(entry);
            try (BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(file.toPath()))) {
                int size = 1024;
                byte[] buff = new byte[size];
                int read;
                while ((read = bis.read(buff, 0, size)) != -1) {
                    zos.write(buff, 0, read);
                }
            }
            zos.closeEntry();
            return;
        }
        for (File children : file.listFiles()) {
            fileToZip(basePath, children, zos);
        }
    }

    public static void filesToZip(File zip, File... files) throws IOException {
        if (files == null || files.length == 0) {
            return;
        }
        filesToZip(zip, Arrays.asList(files));
    }

    public static void filesToZip(File zip, List<File> files) throws IOException {
        if (files == null || files.isEmpty()) {
            return;
        }
        try (ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(zip.toPath())))) {
            for (File file : files) {
                filesToZip(zos, file);
            }
        }
    }

    private static void filesToZip(ZipOutputStream zos, File file) throws IOException {
        if (file.isFile()) {
            ZipEntry entry = new ZipEntry(file.getName());
            zos.putNextEntry(entry);
            try (BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(file.toPath()))) {
                int size = 1024;
                byte[] buff = new byte[size];
                int read;
                while ((read = bis.read(buff, 0, size)) != -1) {
                    zos.write(buff, 0, read);
                }
            }
            zos.closeEntry();
            return;
        }
        for (File children : file.listFiles()) {
            filesToZip(zos, children);
        }
    }

}
